layout: false
---

<!DOCTYPE html>
<html lang="en">

<head>
    <link rel="icon" href="../favicon.ico" type="image/x-icon">
    <meta charset="UTF-8">
    <title>音频可视化 - WR</title>
    <style>
        #file {
            position: fixed;
            top: 10px;
            left: 10px;
            z-index: 100;
            text-align: center;
            margin: 0 auto;
        }

        #canvas {
            position: fixed;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
        }

        #audio {
            position: fixed;
            left: 10px;
            bottom: 10px;
            width: calc(100% - 20px);
            opacity: 0.7;
        }
    </style>
</head>

<body>

    <div id="content">
        <input type="file" id="file" accept="audio/*" />
        <canvas id="canvas"></canvas>
        <audio id="audio" controls></audio>
    </div>
    <script type="text/javascript">

        window.onload = function () {

            var file = document.getElementById("file");
            var audio = document.getElementById("audio");

            file.onchange = function () {
                //part1: 画布
                var canvas = document.getElementById("canvas");
                var context = canvas.getContext("2d");

                canvas.width = window.innerWidth;
                canvas.height = window.innerHeight;
                var WIDTH = canvas.width;
                var HEIGHT = canvas.height;

                //part2: 音频
                var files = this.files;//声音文件
                audio.src = URL.createObjectURL(files[0]);
                audio.load();

                //part3: 分析器
                var AudCtx = new AudioContext();//音频内容
                var src = AudCtx.createMediaElementSource(audio);
                var analyser = AudCtx.createAnalyser();

                src.connect(analyser);
                analyser.connect(AudCtx.destination);
                analyser.fftSize = 128;//快速傅里叶变换, 必须为2的N次方

                var bufferLength = analyser.frequencyBinCount;// = fftSize * 0.5

                //part4: 变量
                var barWidth = (WIDTH / bufferLength) - 1;//间隔1px
                var barHeight;

                var dataArray = new Uint8Array(bufferLength);//8位无符号定长数组

                //part5: 动态监听
                function renderFrame() {
                    requestAnimationFrame(renderFrame);//方法renderFrame托管到定时器，无限循环调度，频率<16.6ms/次

                    context.fillStyle = "#fafafa";//白色背景
                    context.fillRect(0, 0, WIDTH, HEIGHT);//画布拓展全屏,动态调整

                    analyser.getByteFrequencyData(dataArray);//获取当前时刻的音频数据

                    //part6: 绘画声压条
                    var x = 0;
                    for (var i = 0; i < bufferLength; i++) {
                        var data = dataArray[i];//int,0~255

                        var percentV = data / 255;//纵向比例
                        var percentH = i / bufferLength;//横向比例

                        barHeight = HEIGHT * percentV;

                        //gbk,0~255
                        var r = 255 * percentH;//值越大越绿
                        var g = 255 * percentV;//越靠右越红
                        var b = 50;

                        context.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
                        context.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);

                        x += barWidth + 1;//间隔1px
                    }
                }

                renderFrame();

                //part7: 播放声音
                audio.play();
            };
        };
    </script>
</body>

</html>